package com.ludomancy.timescape;

import java.awt.Graphics;
import java.io.DataInputStream;
import java.io.InputStream;

import com.ludomancy.gamelib.GameApplet;
import com.ludomancy.gamelib.ImageStrip;
import com.ludomancy.gamelib.Matrix;
import com.ludomancy.gamelib.ResourceManager;

public class Planet implements Cloneable
{
	public int width;
	public int height;
	
	public static final byte TERRAIN_TYPE_BORDER_SE = 0;
	public static final byte TERRAIN_TYPE_BORDER_S 	= 1;
	public static final byte TERRAIN_TYPE_BORDER_SW = 2;
	public static final byte TERRAIN_TYPE_BORDER_E	= 3;
	public static final byte TERRAIN_TYPE_PLAIN_HIGH = 4;
	public static final byte TERRAIN_TYPE_BORDER_W = 5;
	public static final byte TERRAIN_TYPE_BORDER_NE = 6;
	public static final byte TERRAIN_TYPE_BORDER_N = 7;
	public static final byte TERRAIN_TYPE_BORDER_NW = 8;
	public static final byte TERRAIN_TYPE_HOLE_NW = 9;
	public static final byte TERRAIN_TYPE_HOLE_NE = 10;
	public static final byte TERRAIN_TYPE_HOLE_SW = 11;
	public static final byte TERRAIN_TYPE_HOLE_SE = 12;
	public static final byte TERRAIN_TYPE_PLAIN_LOW = 13;
	
	public static final byte TERRAIN_TYPE_PLAIN_VARIATION_1 = 14;
	public static final byte TERRAIN_TYPE_PLAIN_VARIATION_2 = 15;
	public static final byte TERRAIN_TYPE_PLAIN_VARIATION_3 = 16;
	
	public byte[][] mapType;
	public double[][] water;
	public double[][] grass;
	
	private ImageStrip sandTiles;
	private ImageStrip grassTiles;
	private ImageStrip waterTiles;
	private ImageStrip waterTransitionTiles;
	
	public Planet(String mapFile)
	{
		InputStream is = GameApplet.gameAppletInstance.getClass().getResourceAsStream(mapFile);
		DataInputStream dis = new DataInputStream(is);
		try
		{
			this.width = Integer.parseInt(dis.readLine().trim());
			this.height = Integer.parseInt(dis.readLine().trim());

			mapType = new byte[width][height];
			water = new double[width][height];
			grass = new double[width][height];
			
			String mapData = dis.readLine().trim();
			String[] tilesData = mapData.split(",");
			int i = 0;
			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)	
				{
					byte type = (byte) Integer.parseInt(tilesData[i++]);
					mapType[x][y] = type;
					water[x][y] = 0; // no initial water
					grass[x][y] = 0; // no initial grass
				}
			}
			dis.close();
			
			// load tilesets
			grassTiles = new ImageStrip(ResourceManager.getImage("terrain-grass.png"), Game.TILE_SIZE, Game.TILE_SIZE);
			sandTiles = new ImageStrip(ResourceManager.getImage("terrain-sand.png"), Game.TILE_SIZE, Game.TILE_SIZE);
			waterTiles = new ImageStrip(ResourceManager.getImage("terrain-water.png"), Game.TILE_SIZE, Game.TILE_SIZE);
			waterTransitionTiles = new ImageStrip(ResourceManager.getImage("terrain-water-transition.png"), Game.TILE_SIZE, Game.TILE_SIZE);
			
		}
		catch (Exception e)
		{
		}
	}
	
	public byte getTerrainHeight(int tx, int ty)
	{
		
	}
	
	public byte getTerrainType(int tx, int ty)
	{
		// map all detail tiles to the canonical type
		switch(mapType[tx][ty])
		{
			case TERRAIN_TYPE_PLAIN_VARIATION_1:
			case TERRAIN_TYPE_PLAIN_VARIATION_2:
			case TERRAIN_TYPE_PLAIN_VARIATION_3:
				return TERRAIN_TYPE_PLAIN_LOW;
		}
		return mapType[tx][ty];
	}
	
	public void paint(Graphics g)
	{
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				int px = x * Game.TILE_SIZE;
				int py = y * Game.TILE_SIZE;
				
				// base floor
				sandTiles.paint(g, mapType[x][y], px, py);
				
				// grass
				if(grass[x][y] > 0)
				{
					grassTiles.paint(g, mapType[x][y], px, py);
				}
				
				// water
				if(water[x][y] > 0)
				{
					waterTiles.paint(g, mapType[x][y], px, py);
				}
			}
		}
	}
	
	protected Object clone() throws CloneNotSupportedException
	{
		Object obj = null;
		try
		{
			obj = super.clone();
			Planet newmap = (Planet) obj;
			newmap.mapType = Matrix.clone(mapType);
			newmap.water = Matrix.clone(water);
		} 
		catch (CloneNotSupportedException ex)
		{
		}
		return obj;
	}

	public void addWater(int tx, int ty)
	{
		water[tx][ty] += 1;
		// now, process the excesses
		boolean[][] tabu = new boolean[width][height];
		addWater(tx, ty, tabu, water[tx][ty]);
	}
	
	public boolean isWithin(int tx, int ty)
	{
		return tx >= 0 && tx < width && ty >= 0 && ty < height;
	}
	
	private double getSlope(int from_x, int from_y, int to_x, int to_y)
	{
		int type = getTerrainType(to_x, to_y);
		switch(type)
		{
			case TERRAIN_TYPE_BORDER_SE:
			case TERRAIN_TYPE_BORDER_S:
			case TERRAIN_TYPE_BORDER_SW:
			case TERRAIN_TYPE_BORDER_E:
			case TERRAIN_TYPE_PLAIN_HIGH:
			case TERRAIN_TYPE_BORDER_W:
			case TERRAIN_TYPE_BORDER_NE:
			case TERRAIN_TYPE_BORDER_N:
			case TERRAIN_TYPE_BORDER_NW:
			case TERRAIN_TYPE_HOLE_NW:
			case TERRAIN_TYPE_HOLE_NE:
			case TERRAIN_TYPE_HOLE_SW:
			case TERRAIN_TYPE_HOLE_SE:
			case TERRAIN_TYPE_PLAIN_LOW:
		}
	}
	
	private boolean isPassableByWater(int from_x, int from_y, int to_x, int to_y)
	{
		// take into account the cases in which it doesn't
		int type = getTerrainType(to_x, to_y);
		
//		return getTerrainType(tx, ty) == TERRAIN_TYPE_PLAIN;
	}
	
	private void addWater(int tx, int ty, boolean[][] tabu, double quantity)
	{
		if(isWithin(tx, ty))
		{
			if(!tabu[tx][ty])
			{
				tabu[tx][ty] = true;
				water[tx][ty] += quantity;
				// get the current excess of this tile
				double excess = water[tx][ty] - 0.2;
				if(excess > 0)
				{
					int available = 0;
					double amount = 0;
					if(isWithin(tx-1, ty) && isPassableByWater(tx, ty, tx-1,ty))
					{
						available |= 1;
						amount++;
					}

					if(isWithin(tx+1, ty) && isPassableByWater(tx, ty, tx+1,ty))
					{
						available |= 2;
						amount++;
					}
					
					if(isWithin(tx, ty-1) && isPassableByWater(tx, ty, tx,ty-1))
					{
						available |= 4;
						amount++;
					}

					if(isWithin(tx, ty+1) && isPassableByWater(tx, ty, tx,ty+1))
					{
						available |= 8;
						amount++;
					}
					excess = excess / amount;
					if((available & 1) != 0)
						addWater(tx-1, ty, tabu, excess);
					if((available & 2) != 0)
						addWater(tx+1, ty, tabu, excess);
					if((available & 4) != 0)
						addWater(tx, ty-1, tabu, excess);
					if((available & 8) != 0)
						addWater(tx, ty+1, tabu, excess);
				}
			}
		}
	}

}
